﻿using DevExpress.XtraGrid.Views.Grid;
using System;
using Barkod.Utility;
using System.Windows.Forms;
using System.Data;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraEditors.Repository;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Data.SqlClient;
using System.IO;
using System.Xml;
using System.Globalization;
using Barkod;
using DevTeam;

namespace Extensions
{
    public static class GridViewExtensions
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="gridView"></param>
        /// <param name="pairs">OldColumn:NewColumn , OldColumn:NewColumn , ...</param>
        public static void SetCaptions(this GridView gridView, params string[] pairs)
        {
            string problematicCols = string.Empty;
            foreach (string pair in pairs)
            {
                try
                {
                    string[] vals = pair.Split(':');
                    string colName = vals[0].Trim();
                    string caption = vals[1].Trim();
                    gridView.Columns[colName].Caption = caption;
                }
                catch
                {
                    problematicCols += pair + ",";
                }
            }
            
            if (problematicCols != string.Empty)
            {
                ApplicationException aex = new ApplicationException("Error in column naming! Column: " + problematicCols.RemoveIfEndsWith(","));
            }
        }

        private static BindingSource GetBS(DataTable dt)
        {
            DataSet ds = dt.DataSet;
            if (ds == null)
            {
                ds = new DataSet();
                ds.Tables.Add(dt);
            }
            int index = 0;
            for (int i = 0; i < ds.Tables.Count; i++)
            {
                if (ds.Tables[i] == dt)
                {
                    index = i;
                    break;
                }
            }
            return GetBS(ds, index);
        }

        private static BindingSource GetBS(DataSet ds, int tableIndex)
        {
            string tableName = ds.Tables[tableIndex].TableName;
            if (tableName == string.Empty)
                tableName = "table" + tableIndex;
            BindingSource bs = new BindingSource(ds, tableName);
            return bs;
        }

        public static BindingSource Init(this GridView gridView, string SPName, params object[] SPParams)
        {
            DataSet ds = AppSession.DataManager.ExecuteDataSet(SPName, SPParams);
            BindingSource bs = GetBS(ds, 0);
            return SetBs(gridView, bs);
        }

        public static void SetIdentity(this GridView gridView, string ColumnName)
        {
            DataColumn dc = gridView.Dataset().Tables[0].Columns[ColumnName];
            dc.AutoIncrement = true;
            dc.AutoIncrementSeed = -1;
            dc.AutoIncrementStep = -1;
        }

        public static BindingSource Init(this GridView gridView, DataTable dataTable)
        {
            BindingSource bs = GetBS(dataTable);
            return SetBs(gridView, bs);
        }

        public static void UpdateSchema(this GridView gridView)
        {
            Dictionary<string, bool> visibilities = new Dictionary<string, bool>();
            for (int i = 0; i < gridView.Columns.Count; i++)
            {
                GridColumn gc = gridView.Columns[i];
                if (!visibilities.ContainsKey(gc.FieldName))
                    visibilities.Add(gc.FieldName, gc.Visible);
            }

            BindingSource bs = (BindingSource)gridView.DataSource;
            SetBs(gridView, bs, true);

            foreach (KeyValuePair<string, bool> pair in visibilities)
            {
                gridView.Columns[pair.Key].Visible = pair.Value;
            }
        }

        public static void AddColumn(this GridView gridView, string name, Type type, string expression, int TableIndex)
        {
            if (expression.Contains("{FK}"))
            {
                string FKName = string.Empty;

                if (expression.ToLower().Contains("child"))
                    FKName = gridView.DataTable().ChildRelations[TableIndex].RelationName;
                if (expression.ToLower().Contains("parent"))
                    FKName = gridView.DataTable().ParentRelations[TableIndex].RelationName;

                expression = expression.Replace("{FK}", FKName);
            }

            DataTable dataSource = gridView.DataTable();
            dataSource.Columns.Add(name, type);

            if (expression != string.Empty)
                dataSource.Columns[name].Expression = expression;

            UpdateSchema(gridView);
        }

        public static void AddColumn(this GridView gridView, string name, Type type, string expression)
        {
            AddColumn(gridView, name, type, expression, 0);
        }

        public static void ChangeExpression(this GridView gridView, string columnName, string expression)
        {
            if (expression.Contains("{FK}"))
            {
                string FKName = string.Empty;

                if (expression.ToLower().Contains("child"))
                    FKName = gridView.DataTable().ChildRelations[0].RelationName;
                if (expression.ToLower().Contains("parent"))
                    FKName = gridView.DataTable().ParentRelations[0].RelationName;

                expression = expression.Replace("{FK}", FKName);
            }

            DataTable dataSource = gridView.DataTable();
            try
            {
                dataSource.Columns[columnName].Expression = expression;
                UpdateSchema(gridView);
            }
            catch
            {
                ApplicationException aex = new ApplicationException("Error in changing expression! Column: " + columnName);                
            }
        }

        static XForm x = new XForm();
        private static BindingSource SetBs(GridView gridView, BindingSource bs, bool initialized = false)
        {
            x.InitGridView(gridView, bs, initialized);

            foreach (GridColumn gc in gridView.Columns)
            {
                if (gc.ColumnType == typeof(DateTime))
                {
                    gc.DisplayFormat.FormatString = "dd.MM.yyyy HH:mm.ss";
                }
            }
            
            return bs;
        }

        public static BindingSource AddDetailGrid(this GridView gvMaster, GridView gvDetail, string commaSeperatedKeyColumns)
        {
            DataSet dsRoot = null;
            BindingSource bsRoot = (BindingSource)gvMaster.DataSource;

            int dtSourceIndex = -1;
            while (true)
            {
                dtSourceIndex++;
                if (dtSourceIndex > 10)
                {
                    //Pratikte 10 grid birbirine bağlanmış olmamalıdır ;
                    throw new ApplicationException("Binding kullanım hatası !!!");
                }
                if (bsRoot.DataSource.GetType() == typeof(DataSet))
                {
                    dsRoot = (DataSet)bsRoot.DataSource;
                    break;
                }
                else
                {
                    bsRoot = (BindingSource)bsRoot.DataSource;
                }
            }

            DataTable dtMaster = dsRoot.Tables[dtSourceIndex];
            DataTable dtDetail = dsRoot.Tables[dtSourceIndex + 1];

            string[] colNames = commaSeperatedKeyColumns.Split(',');

            List<DataColumn> dcPKs = new List<DataColumn>();
            List<DataColumn> dcFKs = new List<DataColumn>();

            foreach (string colName in colNames)
            {
                dcPKs.Add(dtMaster.Columns[colName]);
                dcFKs.Add(dtDetail.Columns[colName]);
            }

            string relationName = string.Format("FK_{0}_{1}_{2}", gvMaster.Name, gvDetail.Name, commaSeperatedKeyColumns.Replace(',', '_'));
            DataRelation dr = new DataRelation(relationName, dcPKs.ToArray(), dcFKs.ToArray());

            dtMaster.ChildRelations.Add(dr);

            BindingSource bsMaster = (BindingSource)gvMaster.DataSource;
            BindingSource bsDetail = new BindingSource(bsMaster, dr.RelationName);
            return SetBs(gvDetail, bsDetail);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gvMaster"></param>
        /// <param name="gvDetail"></param>
        /// <param name="TableIndex">0 based datatable index</param>
        /// <param name="commaSeperatedKeyColumns"></param>
        /// <returns></returns>
        public static BindingSource AddDetailGrid(this GridView gvMaster, GridView gvDetail, int TableIndex, string commaSeperatedKeyColumns)
        {
            DataSet dsRoot = null;
            BindingSource bsRoot = (BindingSource)gvMaster.DataSource;

            int dtSourceIndex = -1;
            while (true)
            {
                dtSourceIndex++;
                if (dtSourceIndex > 10)
                {
                    //Pratikte 10 grid birbirine bağlanmış olmamalıdır ;
                    throw new ApplicationException("Binding kullanım hatası !!!");
                }
                if (bsRoot.DataSource.GetType() == typeof(DataSet))
                {
                    dsRoot = (DataSet)bsRoot.DataSource;
                    break;
                }
                else
                {
                    bsRoot = (BindingSource)bsRoot.DataSource;
                }
            }

            DataTable dtMaster = dsRoot.Tables[dtSourceIndex];
            DataTable dtDetail = dsRoot.Tables[TableIndex];

            string[] colNames = commaSeperatedKeyColumns.Split(',');

            List<DataColumn> dcPKs = new List<DataColumn>();
            List<DataColumn> dcFKs = new List<DataColumn>();

            foreach (string colName in colNames)
            {
                dcPKs.Add(dtMaster.Columns[colName]);
                dcFKs.Add(dtDetail.Columns[colName]);
            }

            string relationName = string.Format("FK_{0}_{1}_{2}", gvMaster.Name, gvDetail.Name, commaSeperatedKeyColumns.Replace(',', '_'));
            DataRelation dr = new DataRelation(relationName, dcPKs.ToArray(), dcFKs.ToArray());

            dtMaster.ChildRelations.Add(dr);

            BindingSource bsMaster = (BindingSource)gvMaster.DataSource;
            BindingSource bsDetail = new BindingSource(bsMaster, dr.RelationName);
            return SetBs(gvDetail, bsDetail);
        }

        public static DataSet Dataset(this GridView gvMaster)
        {
            DataSet dsRoot = null;
            BindingSource bsRoot = (BindingSource)gvMaster.DataSource;

            int dtSourceIndex = -1;
            while (true)
            {
                dtSourceIndex++;
                if (dtSourceIndex > 10)
                {
                    //Pratikte 10 grid birbirine bağlanmış olmamalıdır ;
                    throw new ApplicationException("Too many detail grids !!!");
                }
                if (bsRoot.DataSource.GetType() == typeof(DataSet))
                {
                    dsRoot = (DataSet)bsRoot.DataSource;
                    return dsRoot;
                }
                else
                {
                    bsRoot = (BindingSource)bsRoot.DataSource;
                }
            }
        }

        public static DataTable DataTable(this GridView gvMaster)
        {
            DataSet dsRoot = null;
            BindingSource bsRoot = (BindingSource)gvMaster.DataSource;

            int dtSourceIndex = -1;
            while (true)
            {
                dtSourceIndex++;
                if (dtSourceIndex > 10)
                {
                    //Pratikte 10 grid birbirine bağlanmış olmamalıdır ;
                    throw new ApplicationException("Too many detail grids !!!");
                }
                if (bsRoot.DataSource.GetType() == typeof(DataSet))
                {
                    dsRoot = (DataSet)bsRoot.DataSource;
                    break;
                }
                else
                {
                    bsRoot = (BindingSource)bsRoot.DataSource;
                }
            }
            return dsRoot.Tables[dtSourceIndex];
        }

        public static void UpdateData(this GridView gridView, string SPName, params object[] SPParams)
        {
            DataSet dsRoot = gridView.Dataset();
            dsRoot.Clear();

            DataSet dsNew = AppSession.DataManager.ExecuteDataSet(SPName, SPParams);

            dsRoot.Merge(dsNew);                        
        }

        public static void UpdateData(this GridView gridView, DataTable dt)
        {
            DataSet dsRoot = gridView.Dataset();
            dsRoot.Tables[dt.TableName].Clear();
            dsRoot.Merge(dt);            
        }

        public static void UpdateData(this GridView gridView, bool ClearOldData, DataTable dt)
        {
            DataSet dsRoot = gridView.Dataset();
            if (ClearOldData)
            {
                dsRoot.Tables[dt.TableName].Clear();
            }
            dsRoot.Merge(dt);            
        }

        public static void UpdateData(this GridView gridView, bool ClearOldData, string SPName, params object[] SPParams)
        {
            DataSet dsRoot = gridView.Dataset();
            if (ClearOldData)
            {
                dsRoot.Clear();
            }

            DataSet dsNew = AppSession.DataManager.ExecuteDataSet(SPName, SPParams);

            dsRoot.Merge(dsNew);            
        }

        public static int Update(this GridView gridView, string ColumnNames = "", string SourceSqlTableName = "")
        {
            if (SourceSqlTableName == "")
            {
                List<string> columnNames = new List<string>();
                foreach (DataColumn col in gridView.DataTable().Columns)
                {
                    if (col.Expression == string.Empty)
                        columnNames.Add(col.ColumnName);
                }
                columnNames.Sort();
                string result = string.Join(",", columnNames.ToArray());
                DataTable tables = AppSession.DataManager.ExecuteDataTable("GetTableByColumns", result);
                if (tables.Rows.Count > 1)
                {
                    throw new ApplicationException("There are more than 1 table with same schema. Please pass the table name as paramater.");
                }
                else
                {
                    SourceSqlTableName = tables.Rows[0][0].ToString();
                }
            }

            string colNames = string.Empty;

            if (ColumnNames == "")
            {
                colNames = "*";
            }
            else
            {
                colNames = ColumnNames;
            }

            SqlDataAdapter da = new SqlDataAdapter(string.Format("SELECT {0} FROM {1}", colNames, SourceSqlTableName), AppSession.DataManager.ConnectionString);
            SqlCommandBuilder builder = new SqlCommandBuilder(da);
            builder.QuotePrefix = "[";
            builder.QuoteSuffix = "]";
            builder.SetAllValues = false;
            da.UpdateCommand = builder.GetUpdateCommand();
            DataTable changes = gridView.DataTable().GetChanges();
            if (changes != null && changes.Rows.Count > 0)
            {
                gridView.DataTable().AcceptChanges();
                return da.Update(changes);
            }
            else
                return 0;
        }

        public static void AddSummary(this GridView gridView, params string[] columnNames)
        {
            gridView.OptionsView.ShowFooter = true;
            foreach (GridColumn col in gridView.Columns)
            {
                if (col.Visible == true)
                {
                    col.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Count;
                    col.SummaryItem.DisplayFormat = "{0:#,##0} records";
                    break;
                }
            }

            foreach (string columnName in columnNames)
            {
                if (gridView.Columns[columnName] != null)
                {
                    GridColumn gc = gridView.Columns[columnName];

                    gc.SummaryItem.FieldName = columnName;
                    gc.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum;
                    gc.SummaryItem.DisplayFormat = "{0:#,##0.#}";
                }
            }            
        }


        public static void VeriyiGuncelle(this GridView gridView, string SPName, params object[] SPParams)
        {
            DataSet dsRoot = gridView.Dataset();
            dsRoot.Clear();

            DataSet dsNew = AppSession.DataManager.ExecuteDataSet(SPName, SPParams);

            dsRoot.Merge(dsNew);
            //gridView.BestFitColumns();            
        }

        public static void VeriyiGuncelle(this GridView gridView, DataTable dt)
        {
            DataSet dsRoot = gridView.Dataset();
            dsRoot.Tables[dt.TableName].Clear();
            dsRoot.Merge(dt);
            //gridView.BestFitColumns();
        }

        public static void VeriyiGuncelle(this GridView gridView, bool ClearOldData, DataTable dt)
        {
            DataSet dsRoot = gridView.Dataset();
            if (ClearOldData)
            {
                dsRoot.Tables[dt.TableName].Clear();
            }
            dsRoot.Merge(dt);
            //gridView.BestFitColumns();
        }

        public static void VeriyiGuncelle(this GridView gridView, bool ClearOldData, string SPName, params object[] SPParams)
        {
            DataSet dsRoot = gridView.Dataset();
            if (ClearOldData)
            {
                dsRoot.Clear();
            }

            DataSet dsNew = AppSession.DataManager.ExecuteDataSet(SPName, SPParams);

            dsRoot.Merge(dsNew);
            //gridView.BestFitColumns();
        }


        public static void MakeColumnInvisible(this GridView gridView, params string[] columnNames)
        {
            string problematicCols = string.Empty;
            if (columnNames.Length == 0)
            {
                foreach (GridColumn gc in gridView.Columns)
                    gc.Visible = false;
            }
            else
            {
                foreach (string columnName in columnNames)
                {
                    try
                    {
                        gridView.Columns[columnName].Visible = false;
                    }
                    catch
                    {
                        problematicCols += columnName + ",";
                    }
                }
            }

            if (problematicCols != string.Empty)
            {                
                ApplicationException aex = new ApplicationException("Error in column hiding! Column: " + problematicCols.RemoveIfEndsWith(","));
            }
        }

        public static void MakeColumnVisibleWithCaptions(this GridView gridView, params string[] pairs)
        {
            int i = 0;
            foreach (string pair in pairs)
            {
                string[] names = pair.Split(':');

                gridView.Columns[names[0]].VisibleIndex = i++;
                gridView.Columns[names[0]].Caption = names[1];
            }
        }

        public static void MakeRowSelectable(this GridView gridView)
        {
            gridView.OptionsSelection.EnableAppearanceFocusedCell = false;
            gridView.OptionsSelection.EnableAppearanceFocusedRow = true;
            gridView.OptionsSelection.MultiSelect = false;
        }

       

        public static void MakeReadOnly(this GridView gridView, params string[] ExceptTheseColumns)
        {
            foreach (GridColumn gc in gridView.Columns)
                gc.OptionsColumn.AllowEdit = ExceptTheseColumns.Any(gc.FieldName.Contains);

            if (ExceptTheseColumns.Length == 0)
                gridView.OptionsBehavior.ReadOnly = true;
        }

        public static void MakeEditable(this GridView gridView, params string[] ExceptTheseColumns)
        {
            foreach (GridColumn gc in gridView.Columns)
                gc.OptionsColumn.AllowEdit = !ExceptTheseColumns.Any(gc.FieldName.Contains);
        }

        public static void FormatAsPercentage(this GridView gridView, params string[] columnNames)
        {
            string problematicCols = string.Empty;

            foreach (string columnName in columnNames)
            {
                try
                {
                    RepositoryItemProgressBar pb = new RepositoryItemProgressBar();
                    pb.ShowTitle = true;
                    pb.PercentView = true;
                    pb.Maximum = 100;

                    gridView.Columns[columnName].ColumnEdit = pb;
                }
                catch
                {
                    problematicCols += columnName + ",";
                }
            }

            if (problematicCols != string.Empty)
            {
                ApplicationException aex = new ApplicationException("Error in column formating! Column: " + problematicCols.RemoveIfEndsWith(","));                
            }
        }

        public static void SetFilterTypeToCheckedList(this GridView gridView, params string[] columnNames)
        {
            string problematicCols = string.Empty;

            foreach (string columnName in columnNames)
            {
                try
                {
                    GridColumn col = gridView.Columns[columnName];
                    col.OptionsFilter.FilterPopupMode = FilterPopupMode.CheckedList;
                    col.FilterMode = DevExpress.XtraGrid.ColumnFilterMode.Value;
                    col.OptionsFilter.ImmediateUpdateAutoFilter = true;
                    col.OptionsFilter.ImmediateUpdatePopupDateFilterOnCheck = DevExpress.Utils.DefaultBoolean.True;
                    col.OptionsFilter.ImmediateUpdatePopupDateFilterOnDateChange = DevExpress.Utils.DefaultBoolean.True;                    
                }
                catch
                {
                    problematicCols += columnName + ",";
                }
            }

            if (problematicCols != string.Empty)
            {
                ApplicationException aex = new ApplicationException("Error in changing column filter type to checked list! Column: " + problematicCols.RemoveIfEndsWith(","));                                                
            }
        }

        public static void FormatAsDate(this GridView gridView, params string[] columnNames)
        {
            string problematicCols = string.Empty;

            foreach (string columnName in columnNames)
            {
                try
                {
                    GridColumn col = gridView.Columns[columnName];
                    col.DisplayFormat.FormatType = DevExpress.Utils.FormatType.DateTime;
                    col.DisplayFormat.FormatString = "dd.MM.yyyy";
                }
                catch
                {
                    problematicCols += columnName + ",";
                }
            }

            if (problematicCols != string.Empty)
            {
                ApplicationException aex = new ApplicationException("Error in column formatting! Column: " + problematicCols.RemoveIfEndsWith(","));
            }
        }

        public static void BringSideBySide(this GridView gridView, params string[] pairs)
        {
            foreach (string pair in pairs)
            {
                string[] cols = pair.Split(':');
                gridView.Columns[cols[0]].VisibleIndex = gridView.Columns[cols[1]].VisibleIndex;
            }
        }

        public static void BringSideToBase(this GridView gridView, string BaseColumn, params string[] pairs)
        {
            int baseIndex = gridView.Columns[BaseColumn].VisibleIndex;
            foreach (string column in pairs)
            {
                gridView.Columns[column].VisibleIndex = ++baseIndex;
            }
        }

        public static void FormatAsNumber(this GridColumn gc, int decimalPoints)
        {
            ((GridView)gc.View).FormatAsNumber(decimalPoints,gc.Name);
        }

        public static void FormatAsNumber(this GridView gridView, int decimalPoints, params string[] columnNames)
        {
            string problematicCols = string.Empty;

            foreach (string columnName in columnNames)
            {
                try
                {
                    GridColumn col = gridView.Columns[columnName];
                    col.DisplayFormat.FormatType = DevExpress.Utils.FormatType.Numeric;
                    string format = "#,###.";
                    for (int i = 0; i < decimalPoints; i++)
                        format += "0";

                    if (format.EndsWith("."))
                        format = format.Substring(0, format.Length - 1);
                    col.DisplayFormat.FormatString = format + ";-" + format + "; ";

                    col.FilterMode = DevExpress.XtraGrid.ColumnFilterMode.Value;
                }
                catch
                {
                    problematicCols += columnName + ",";
                }
            }

            if (problematicCols != string.Empty)
            {
                ApplicationException aex = new ApplicationException("Error in column formatting! Column: " + problematicCols.RemoveIfEndsWith(","));                
            }
        }

        public static string GetChangesAsXml(this GridView gridView, string TableName, params bool[] BringAll)
        {
            StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
            XmlTextWriter tw = new DataManager.KarbelXmlWriter(sw);

            DataTable dt = gridView.DataTable();
            if (dt.DataSet == null)
            {
                DataSet ds = new DataSet("AppDS");
                ds.Tables.Add(dt);
            }
            else
                dt.DataSet.DataSetName = "AppDS";

            DataSet dsChanges;
            if (BringAll.Length>0)
                dsChanges = dt.DataSet;
            else
                dsChanges = dt.DataSet.GetChanges();
            if (dsChanges == null)
                return "";

            DataTable dtChanges = dsChanges.Tables[dt.TableName];

            for (int i = dtChanges.Columns.Count - 1; i >= 0; i--)
            {
                if (dtChanges.Columns[i].DataType == typeof(System.Byte[]))
                    dtChanges.Columns.RemoveAt(i);
                else if (dtChanges.Columns[i].DataType == typeof(DateTime))
                    dtChanges.Columns[i].DateTimeMode = DataSetDateTime.Unspecified;
            }

            dtChanges.TableName = TableName;
            dtChanges.DataSet.WriteXml(tw, XmlWriteMode.IgnoreSchema);
           
            return sw.ToString();
        }
    }
}